Chapter 6
The MFC Application Object, Message Routing, and Idle Processing

by David Lowndes

In This Chapter

  The MFC Application Object 242
  Message Routing, Message Maps, and Message Categories 259
  Idle Processing 266
  The Splash Screen Component 270

In this chapter, you will learn about the CWinApp object. In doing so, I hope to encourage you to examine the MFC source code and discover what happens beneath the surface of your MFC application.

The MFC Application Object

The CWinApp class is the starting point of your MFC application. It controls the initialization and startup operations, runs the main message loop, and handles shutdown. CWinApp can handle command messages in the same way as your view and document classes, and it contains several important public member variables that set the help file, the root registry key, and other system-wide settings for your application.

A CWinApp object never appears directly in your application; instead, it is the base class of your application object. This is a global object named theApp, created by AppWizard in your main application source file. If you use AppWizard to create a project named MyProject, your application class is named CMyProjectApp and the theApp object is declared in MyProject.cpp.

Because it is a global variable, this object is instantiated along with any other global C++ objects as part of the runtime startup code. You can access it from anywhere in your source code using the AfxGetApp function like this:

    CMyApp * pApp = static_cast<CMyApp*>( AfxGetApp() );

Because the object is a global variable, you might wonder why MFC exposes it this way rather than providing an extern definition in a header file. In the context of your executable, you could just as well access the object directly, but in the context of an MFC DLL, it is significant. Listing 6.1, a code snippet of an exported function in an MFC DLL, illustrates the point.

Listing 6.1 AfxGetApp and Application Module State


BOOL PASCAL ExportedFunction1( )
{
    CWinApp * pApp = AfxGetApp();
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    pApp = AfxGetApp();

The returned pApp object pointer from the first call to AfxGetApp is the calling application’s CWinApp object. The result of the second call to AfxGetApp is the DLL’s CWinApp object. This apparent switch is accomplished by the AFX_MANAGE_STATE macro. It constructs an object that chains a module state to the calling application’s module data, so that in this example, the DLL module state (returned by the call to AfxGetStaticModuleState) is linked to the application. There’s no need to remember to reset these state variables; it’s done automatically by the object’s destructor when the function exits.

It’s common to use AFX_MANAGE_STATE in any exported MFC DLL functions where you want the program to use a resource from the DLL.

If you are writing an EXE application, you can add the following extern definition to your application object’s header file and access the global theApp object directly in any source file that includes the header file:

extern CMyProjectApp theApp;

CWinApp and Application Lifetime

As mentioned previously, the CWinApp-derived object is a global variable, and therefore exists for the lifetime of your program.

The class member variables are initialized by the class’s constructor code. Because the object is global, the constructor is called by the compiler’s runtime startup code, and therefore all the default values are set before the main part of your program code executes.

After global variables and other aspects of the C runtime support are initialized, your program code executes and is essentially as straightforward as the pseudocode in Listing 6.2.

Listing 6.2 Program Execution Sequence


if ( InitInstance() )
{
    Run;
}
else
{
    Destroy the main window if there is one;
}
ExitInstance;

In practice, if you examine the MFC source code in WinMain.cpp, you’ll find that it’s a little more involved, but this simple representation expresses the general idea.

It’s rare that you’ll need to know about or override the default implementations of Run or ExitInstance, but it’s almost mandatory for you to modify the boilerplate code for your application’s InitInstance.

InitInstance

InitInstance performs all the application-specific initialization. I’ll cover this in depth later in the chapter (see “InitInstance—Application-Specific Initialization”).

Run

Run is the heart of your application and is where your application spends the vast majority of its processing life. This function is a loop that retrieves and processes messages from your application’s message queue. If there are no messages in the queue, it calls the OnIdle function. If there is a message, and it’s not WM_QUIT, it is dispatched into the MFC message processing code. If the message is WM_QUIT, the loop ends and the function returns. You can think of the Run function as something like the pseudocode in Listing 6.3.

Listing 6.3 Run() Pseudocode


for (;;)
{
    MSG msg;
    while ( !PeekMessage( &msg, ...) )
    {
        OnIdle();
    }

    if ( msg.message == WM_QUIT )
    {
        return ExitInstance();
    }
    else
    {
        DispatchMessage( &msg );
    }
}

I’ll discuss the path that dispatched messages take later on in this chapter under the “Message Routing” topic.

ExitInstance

You’re only likely to need to override the ExitInstance member function if you need to free some special allocated resource that your application used. The return value from ExitInstance is the exit code value that your application returns on termination. The normal convention is to return zero if the application shuts down normally, and any other value to indicate an error.


Tip:  

If you override ExitInstance, be sure to call the base class function because it saves your application Registry settings and performs necessary cleanup of resources used by MFC.


OnIdle

Override the OnIdle function if you need to perform background processing in your application. OnIdle is covered in more depth under “Idle Processing” later in the chapter.

The CWinApp Data Members

The CWinApp data members are documented in the Visual C++ help, but the help doesn’t give any examples of typical values that these variables take. I’ve included a few examples of those public member variables that you might find useful in your own programs.


Note:  

Some of these members are actually declared in CWinThread (which is the base class of CWinApp), but for now, you can regard them as being part of CWinApp.




m_pszAppName

The m_pszAppName variable contains the name of the application as a string, for example, “MyProject”. MFC displays this string for the caption bar text if you use AfxMessageBox.

If you look at the help for the CWinApp constructor, you’ll find that it accepts a string parameter. In the boilerplate code generated by AppWizard, this is NULL. This NULL value instructs MFC to use the AFX_IDS_APP_TITLE string resource to initialize the m_pszAppName member. However, if you want to, you can modify the line of code that constructs this object to set this variable this way:

CMyProjectApp::CMyProjectApp() : CWinApp(“I want to set this string as the
Äapplication name”)
{
}

m_hInstance

Although the Visual C++ help describes m_hInstance as the current instance of the application, the concept of an instance handle is a relic from 16-bit Windows, and it doesn’t exist as such under Win32. It is better described as a module handle because it’s actually the load address of the module in the process’s address space.

For a default Visual C++ project, an EXE module will have the value 0x00400000, whereas a DLL would have 0x10000000. The linker assigns these values. You can override them from your project’s Link settings.


Note:  

The EXE module values can be different at runtime because the Win32 loader can relocate a module if the default address space is already in use by another module.


m_hPrevInstance

m_hPrevInstance is a relic from 16-bit Windows applications, where it is used to indicate the previous instance handle of a running application. Under Win32, it is always NULL.

Under 16-bit Windows, you could use this variable to restrict an application to a single instance. Under Win32, you can achieve the same result using a mutex, as illustrated in Listing 6.4.

Listing 6.4 Using a Mutex to Limit an Application to a Single Instance


BOOL CDlgApp::InitInstance()
{
    bool bAlreadyRunning;

    HANDLE hMutexOneInstance = CreateMutex( NULL, TRUE,
                        “18A5330D_9DCA_11D2_9847_006052028C2E” );
    bAlreadyRunning = ( GetLastError() == ERROR_ALREADY_EXISTS );
    if ( hMutexOneInstance )
    {
        ReleaseMutex( hMutexOneInstance );
    }
    if ( bAlreadyRunning )
    {
        AfxMessageBox( “Already running” );
        return FALSE;
    }
    ...

The CreateMutex call is an atomic operation and guarantees that this operation is fail-safe.


Note:  

The string I’ve used in CreateMutex is a GUID generated by the MFC AppWizard in the header file for my project. If you use this technique, be sure to use your own guaranteed unique string from your own project’s header file.


m_lpCmdLine

m_lpCmdLine is a pointer to the null-terminated command-line string. This string is the portion of the command line after the executable name. There is no additional processing of the command line, so all white space characters between parameters are intact.

For example, if you ran the program from the following command line

>C:\Tests\MyApp -a -b   c:\Tests\filename.ext

this member would be “-a -b c:\Tests\filename.ext”.


TIP:  

You might find the m_lpCmdLine member difficult to use, but don’t forget that you can always make use of the global C runtime __argc and __argv facilities to handle command-line parameters in the same easy way that you could if you were writing a console application. The __argc and __argv variables are global and can be accessed anywhere in your program.


m_nCmdShow

When the shell or another application starts your program, it passes a parameter to suggest the initial show state of the main frame window. This value is eventually passed to the ShowWindow API by way of the m_nCmdShow member variable.

The m_nCmdShow variable takes on different values depending on how you invoked your application. For example, if you start your application by double-clicking the executable in Explorer, the value in m_nCmdShow will instruct ShowWindow to display the window in its visible, nonmaximized state. However, if you invoke the application through a shortcut, you can use the shortcut’s property page to specify the initial window state as Normal, Maximized, or Minimized.

m_bHelpMode

m_bHelpMode indicates whether the application is in help context mode (typically invoked when you press Shift+F1).

m_pActiveWnd

m_pActiveWnd is used as a pointer to the main window of the container application when an OLE server is in-place active. It is NULL if the application is not in-place activated.

m_pMainWnd

m_pMainWnd is used to store a pointer to the application’s top-level window. MFC terminates the application when this window is closed. A consequence of this is discussed under the topic “Dialog Application” later in this chapter.

m_pszExeName

m_pszExeName is the module name of the application, for example, “MYPROJECTAPP”.

m_pszHelpFilePath

m_pszHelpFilePath is the full path to the application’s Help file, for example, “C:\SAMPLES\MYPROJECT\MYPROJECTAPP.HLP”.

m_pszProfileName

m_pszProfileName is the application’s INI filename or Registry key name.

Initially, this is an INI filename, such as “MYPROJECTAPP.INI”. If your application calls SetRegistryKey, this variable becomes a duplicate of the m_pszAppName variable (MyProjectApp). If you prefer your application to store its settings in an INI file rather than the Registry, remove the call to SetRegistryKey in your application’s InitInstance.

m_pszRegistryKey

The m_pszRegistryKey variable is the name of the Registry key for your application’s settings. It is set up in the SetRegistryKey function.

If you examine your application’s InitInstance function, you’ll see that it includes a call to SetRegistryKey with a fixed string. The code comment inserted by AppWizard rightly suggests that you should set this string to your company name.

Internally, MFC concatenates this string with the m_pszProfileName variable to form the Registry key for your application. For example, if you set this value to “MyCompanyName”, your application’s Registry key is “Software\MyCompanyName\MyProjectApp”.

This is used by the CWinApp registry routines such as GetAppRegistryKey, GetProfileInt, GetProfileString, GetProfileBinary, WriteProfileInt, WriteProfileString, and WriteProfileBinary.


Note:  

There’s an alternative form of the SetRegistryKey function that loads this string from a resource:

void SetRegistryKey(UINT nIDRegistryKey);

You might find this form useful if you produce a program that is resold to OEMs who need to rebadge the program with their own application name. Placing the string in the resource file means that you won’t have to modify the source code to accommodate this requirement.




The CWinApp Member Functions

CWinApp includes many member functions that are small wrappers, such as the LoadCursor and LoadIcon routines. These are not of any great interest except to note that they make use of AfxFindResourceHandle, which enables your program to access resources from within the EXE or MFC extension DLLs.

Run, InitInstance, and ExitInstance, are actually members of CWinThread, but for this discussion, you can assume they belong to CWinApp.

Perhaps the most significant member function of CWinApp is the one that performs the application-specific initialization—InitInstance. Unlike most other functions in CWinApp, the AppWizard generates different code for this function depending on the type of application and the options you chose. I will now cover the InitInstance code for the three most common types of MFC applications generated with the default AppWizard options.

InitInstance—Application-Specific Initialization

The code examples in the following sections show the boilerplate code generated by the AppWizard for three types of MFC applications: Listing 6.5 shows a dialog application, Listing 6.6 an SDI doc/view application, and Listing 6.7 an MDI doc/view application.

For clarity, and to emphasize the important aspects, I’ve removed the comments and the less significant lines of code.

Listing 6.5 A Dialog Application’s InitInstance Code


BOOL CDlgApp::InitInstance()
{
    AfxEnableControlContainer();
    CDlgDlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
    }
    else if (nResponse == IDCANCEL)
    {
    }
    return FALSE;
}

The dialog application is quite straightforward. After calling AfxEnableControlContainer to enable ActiveX container support, the application consists of a modal dialog box.


Note:  

One point to note in the dialog application is that it returns FALSE from InitInstance. If you refer back to the Program Execution Sequence shown earlier in Listing 6.2, you’ll see that this bypasses the Run message-processing loop and terminates the application.


Note also that the m_pMainWnd variable is assigned to the modal dialog. This has the side effect of causing MFC to generate a WM_QUIT message when the dialog closes. This in turn gives rise to a common problem. If you want to use another dialog or a message box after the main dialog has closed, these subsequent windows might briefly appear and promptly close. Microsoft’s Knowledge Base article Q138681 gives the answer and suggests either removing the line of code that assigns m_pMainWnd in InitInstance, or setting the m_pMainWnd member to NULL in the dialog’s WM_NCDESTROY (OnNcDestroy) message handler. Because other aspects of a program might need the m_pMainWnd variable, I’d recommend the latter action. If you want to show another modal dialog after the first, duplicate the assignment of m_pMainWnd to this new dialog.

SDI/MDI Document/View Applications

The SDI and MDI application startup code is remarkably similar, so I’ll discuss them together.

Listing 6.6 An SDI Application’s InitInstance Code


BOOL CSdiApp::InitInstance()
{
    AfxEnableControlContainer();
    SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
    LoadStdProfileSettings();
    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CSdiDoc),
        RUNTIME_CLASS(CMainFrame),
        RUNTIME_CLASS(CSdiView));
    AddDocTemplate(pDocTemplate);
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}

Listing 6.7 An MDI Application’s InitInstance Code


BOOL CMdiApp::InitInstance()
{
    AfxEnableControlContainer();
    SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
    LoadStdProfileSettings();
    CMultiDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(
        IDR_MDITYPE,
        RUNTIME_CLASS(CMdiDoc),
        RUNTIME_CLASS(CChildFrame),
        RUNTIME_CLASS(CMdiView));
    AddDocTemplate(pDocTemplate);
    CMainFrame* pMainFrame = new CMainFrame;
    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
        return FALSE;
    m_pMainWnd = pMainFrame;
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
    pMainFrame->ShowWindow(m_nCmdShow);
    pMainFrame->UpdateWindow();
    return TRUE;
}

The InitInstance function of the doc/view applications is quite different from that of the dialog application. They add Registry support, SDI/MDI document/view, and MFC command-line handling.

The differences between an MDI and SDI application are as follows:

1.  MDI and SDI each have their own specific document template classes.
2.  The MDI application explicitly creates its main frame window.
3.  MDI and SDI have a subtly different usage of ShowWindow.

The latter two differences come about because an MDI application has an extra layer of window hierarchy, as shown in Figure 6.1.


Figure 6.1  SDI and MDI window hierarchy.

In the SDI, the document view window has a single-frame window that is the main application window. In the MDI, the main frame window holds the MDI client window that in turn holds the child frame windows. These child frame windows in turn contain the document’s view windows.

Automation Doc/View Application

Adding Automation support to an application incorporates a fair amount of extra boilerplate code to InitInstance. Listing 6.8 shows the InitInstance function for an MDI application with automation support.

Listing 6.8 InitInstance for an MDI Application with OLE Automation Support


BOOL CMdiAutoApp::InitInstance()
{
    if (!AfxOleInit())
    {
        AfxMessageBox(IDP_OLE_INIT_FAILED);
        return FALSE;
    }
    AfxEnableControlContainer();
    SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
    LoadStdProfileSettings();  // Load standard INI file options
    Ä(including MRU)
    CMultiDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(
        IDR_MDIAU1TYPE,
        RUNTIME_CLASS(CMdiAutoDoc),
        RUNTIME_CLASS(CChildFrame), // custom MDI child frame
        RUNTIME_CLASS(CMdiAutoView));
    AddDocTemplate(pDocTemplate);
    m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
    COleTemplateServer::RegisterAll();
    CMainFrame* pMainFrame = new CMainFrame;
    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
        return FALSE;
    m_pMainWnd = pMainFrame;
    m_pMainWnd->DragAcceptFiles();
    EnableShellOpen();
    RegisterShellFileTypes(TRUE);
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
    {
        return TRUE;
    }
    m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
    COleObjectFactory::UpdateRegistryAll();
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
    pMainFrame->ShowWindow(m_nCmdShow);
    pMainFrame->UpdateWindow();
    return TRUE;
}

If you compare the preceding listing with Listing 6.7, you can see that there’s quite a lot more code to this one.



Functionality in InitInstance

To help you understand the functions in InitInstance, I’ve grouped them into their functional areas, as described in the following sections.

OLE Container Support

If you set the ActiveX Controls check box on step 3 of the AppWizard, the AppWizard adds a call to AfxEnableControlContainer in InitInstance, as illustrated in Listing 6.8. This allows your application to host ActiveX controls.

3D Look for Windows NT 3.5x

Prior to Windows 95, the 3D dialog look was implemented in an additional DLL. It was quite easy to make use of this in an application, and MFC wrapped this up behind the scenes, which made it even easier. Windows 95 and subsequent operating systems have implemented this 3D look natively, and you no longer need this extra DLL or the support in MFC.

Because Visual C++ still supports developing applications for versions of Windows earlier than Windows 95, AppWizard defaults to including the code found in Listing 6.9 in InitInstance.

Listing 6.9 3D-Look Support Code


#ifdef _AFXDLL
    Enable3dControls();
#else
    Enable3dControlsStatic();
#endif

If you don’t need to run your application under earlier versions of Windows, you can safely uncheck the 3D Controls check box on the AppWizard or delete these lines of code afterwards. Leaving this code in under Windows 9x and NT 4 has no detrimental effect because the MFC code checks which operating system the program is running under and ignores the facility if it is not needed.

Registry Usage

You can choose to have your application use private INI files or the registry by using the SetRegistryKey function.

Calling SetRegistryKey causes your application to use the Registry rather than a private INI file. If you call this function, the CWinApp profile functions (such as GetProfileString and WriteProfileString) and the application’s most recently used (MRU) filenames are stored in the Registry.

Most Recently Used Files List

When you open documents in your application, the CWinApp object keeps a note of the last documents you used. These are stored in the Registry or your application’s private INI file.

The LoadStdProfileSettings function loads the most recently used filename information and the print preview’s number of pages setting.

SDI and MDI Document/View

The most significant (if not immediately visible) difference between the code in InitInstance for SDI and MDI applications is that they use different document template classes, which are described in the following sections.

CSingleDocTemplate and CMultiDocTemplate

The CSingleDocTemplate and CMultiDocTemplate classes handle the differences between SDI and MDI document management. CSingleDocTemplate can handle a single document of a single type, whereas CMultiDocTemplate can handle many documents of a single type.

AppWizard adds code to InitInstance to create a single document template type for both SDI and MDI applications. In an MDI application, it’s common to need to support several types of documents and views. It’s quite easy to add additional document/views to your application. Using the ClassView, right-click the project and choose New Form. This lets you create a document/view based on a new form. If you don’t want a form view, you can do it yourself by replicating the few lines of code that create and add a new document template to CWinApp (see Listing 6.10).

Listing 6.10 Creating Additional Document Types


    pDocTemplate = new CMultiDocTemplate(
        IDR_MDITYPE1,
        RUNTIME_CLASS(CMdiDoc1),
        RUNTIME_CLASS(CChildFrame),
        RUNTIME_CLASS(CMdiView1));
    AddDocTemplate(pDocTemplate);

Because you will presumably want to differentiate your document types from one another, you’ll need to use a new resource ID. In the preceding code, I’ve used IDR_MDITYPE1. MFC uses this ID in three places:

1.  As a multisection resource string. See Knowledge Base article Q129095 in your Visual C++ help for the details of the format of this string.
2.  As the menu ID for your document type.
3.  As the document icon ID.

The easiest way to identify the usage of this resource is to use the Resource Symbols dialog which is accessed from the View, Resource Symbols menu. Select the existing document resource ID, view its usage, and then duplicate and change the three resources to create the resources for your new type.

Although I’ve cited this as something you might want to do for an MDI application, in fact the same technique also works for SDI.

In addition to the resources, you’ll probably want to create new document and view classes to support your alternative document. To do this, right-click your project in the Class View, choose New Class, and select the appropriate MFC Classes.

DragAcceptFiles

DragAcceptFiles tells Windows that your application window supports drag-and-drop file opening.

EnableShellOpen

EnableShellOpen enables your application to support activation by DDE commands.

If your MDI application is already running, and you activate one of your application’s supported documents from the shell, the shell uses a DDE command to open the document in the instance of your application that is currently running, rather than start a new instance of your program.

RegisterShellFileTypes

RegisterShellFileTypes sets all the Registry command strings that let the Windows shell automatically invoke your application to open and print your supported documents.

Main Frame Window Creation

In the SDI, the frame window is created when the initial document is created, that is, when ProcessShellCommand is called. An MDI application creates its main frame window explicitly in InitInstance and sets the CWinApp m_pMainWnd member to this window.

Automation Support

If you create an application with (OLE) Automation support, you’ll have an application that can be instantiated by other applications through Automation facilities. See Chapter 11, “COM and MFC,” for more information on Automation.

AfxOleInit

The AfxOleInit call initializes COM support in your application.

ConnectTemplate

ConnectTemplate creates a class factory and links it to the document template.

RegisterAll

RegisterAll performs the registration of the application’s class factory objects.

UpdateRegistry

UpdateRegistry puts information in the Registry that identifies your application document types.

UpdateRegistryAll

UpdateRegistryAll registers all the application’s class factory objects in the Registry.

Rich Edit Control Support

If you use the rich edit control in your application, you need to call AfxInitRichEdit to ensure that the rich edit DLL is loaded for your application.



Command-Line Handling

MFC handles a predefined set of standard command-line parameters by the lines of code shown in Listing 6.11, which are common to doc/view architecture applications.

Listing 6.11 Command-Line Handling Code


    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;

As you can see, there’s not much to it. The key is really in the CCommandLineInfo class. It’s parsed by ParseCommandLine (surprisingly), and then “executed” by ProcessShellCommand.

MFC supports the command-line options shown in Table 6.1.

Table 6.1 MFC Command-Line Options

Command Operation

AppName Creates a new file
AppName filename Opens the file
AppName /p filename Prints the file to the default printer
AppName /pt filename Prints the file to the specified
printer driver port printer
AppName /dde Starts up and executes the DDE command
AppName /Automation Starts up as an OLE Automation server
AppName /Embedding Starts up to edit an embedded OLE item
AppName /unregister Removes the application’s standard registry settings from the Registry

The following sections examine the data members of this class to clarify how the class works:

  m_bShowSplash Setting m_bShowSplash to TRUE indicates that a splash screen should be shown. This value defaults to TRUE unless the command line has the /Embedding or /Automation parameters, which invoke the application without a main window. In a plain AppWizard-generated application, m_bShowSplash is useless because nothing ever uses it. To see it in action, you can add the splash screen component to your application. This is covered a little later in the chapter; see “The Splash Screen Component.”
  m_bRunEmbedded The m_bRunEmbedded value is TRUE if the application was started with the /Embedding switch.
  m_bRunAutomated The m_bRunAutomated value is TRUE if the application was started with the /Automation switch.
  m_nShellCommand The m_nShellCommand value is one of the enumerated values—FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, or AppUnregister—that correspond to the options available in Table 6.1. This value is used to determine the operation of the ProcessShellCommand function.
  m_strFileName m_strFileName is the command-line filename parameter. If there isn’t a command-line filename, this string is empty.
  m_strPrinterName If the command is FilePrintTo, m_strPrinterName is the printer name.
  m_strDriverName If the command is FilePrintTo, m_strDriverName is the printer driver name.
  m_strPortName If the command is FilePrintTo, m_strPortName is the printer port name.

Message Routing, Message Maps, and Message Categories

Windows is a message-driven environment that communicates with an application through messages placed in a queue. An application retrieves these queued messages and dispatches them to functions that correspond to the type of destination window. In traditional non-MFC Windows applications, these messages are handled in a large switch statement.The MFC architecture refines this mechanism and handles these messages in a more elegant manner.

Message Routing

Earlier in this chapter, I mentioned that the Run function is the “heart” of your application. Run retrieves messages from your application’s message queue and pumps them into the MFC framework. Run is essentially a traditional message loop, as shown in Listing 6.12.

Listing 6.12 Traditional Message Loop Pseudocode


MSG msg;
while ( GetMessage( &msg, NULL, 0, 0 ) )
{
    DispatchMessage( &msg );
}

When your application calls GetMessage, it relinquishes control to Windows and never returns until Windows has a message for it. If the message returned in the MSG structure isn’t WM_QUIT, the message is then passed to DispatchMessage. DispatchMessage examines the MSG hwnd member and calls the registered window function for that class of window.

MFC can’t alter this fundamental aspect of how Windows works, but it does refine the mechanism so that you might never need to know about registered window procedures. Instead, you write specific functions for those messages that your application classes handle.

If you examine the Run function in the MFC source code, you’ll find this traditional message loop in the CWinThread::PumpMessage function. One key point to note in this function is that MFC calls a PreTranslateMessage function to try to handle the message before calling DispatchMessage. I cover PreTranslateMessage separately because it’s a useful function to know.

If the message isn’t fully handled by the PreTranslateMessage handlers, the message is finally passed to DispatchMessage and on to the target window’s message-handling procedure. The pseudocode in Listing 6.13 illustrates the operation.

Listing 6.13 Message Processing Calls Pseudocode Showing PreTranslateMessage Usage


MSG msg;
while ( GetMessage( &msg, NULL, 0, 0 ) )
{
    if ( !PreTranslateMessage( &msg ) )
    {
        DispatchMessage( &msg );
    }
}

MFC hides raw window message-handling procedures from you, but they’re still there, lurking in the depths. MFC provides its own window procedure in the guise of AfxWndProc. This eventually calls CWnd::OnWndMsg, and it is this function that processes the messages and deals with the next stage of processing: message maps. If you have a look at the source for OnWndMsg, you’ll see that it treats some messages, such as WM_COMMAND and WM_NOTIFY, as special cases before it gets down to working with the message maps.



What Does the Message Routing?

You can’t see the full picture of the command routing from a casual inspection of the MFC source code in your project. From the BEGIN_MESSAGE_MAP, you can see that a derived class defers messages to its base class, but the relationship between a view and its document class isn’t visible. These relationships are embedded in the MFC source code—in a specific class’s implementations of OnCmdMsg, to be precise. For example, if you examine CView::OnCmdMsg, you’ll see that if the view itself doesn’t handle the message, it tries the attached CDocument class.

Following are a few of the embedded relationships. Note that in each of these, the message proceeds to the next stage only if the current class doesn’t handle the message.

CFrameWnd

CFrameWnd first passes the message to the active view, tries to handle the message itself, and finally passes the message to the CWinApp-derived object.

CView

CView first handles the message itself and then passes the message to the document class.

CDocument

CDocument first handles the message itself and then passes the message to its document template class.

CDialog and CPropertySheet

CDialog and CPropertySheet handle the message themselves, pass the message to their parent window, and finally pass the message to their CWinThread object.

For a command message in a doc/view arrangement, the message flow is therefore as follows:

View->Document->Document Template->Frame->Application

PreTranslateMessage

I’ll not delve into the nitty-gritty depths of MFC here, but the gist is that MFC first lets the child-parent window hierarchy try to process the message through their own PreTranslateMessage handlers.

First, the window that the message is intended for has its PreTranslateMessage handler called. If that doesn’t handle the message, the message is then passed to its parent window’s PreTranslateMessage handlers. Each parent window, up to the top-level frame window, gets its chance to handle the message. This makes PreTranslateMessage a key function to remember if you need to handle messages in a slightly unorthodox manner.

For example, in a dialog box, keystroke messages are intended for the child window that has focus, but you might want to do something slightly out of the ordinary—such as have the Enter key function as the Tab key normally does. PreTranslateMessage is a great place to do these sorts of things because you can handle the message before it is dispatched to its default handler. I don’t recommend the example in Listing 6.14 because it goes against the consistency of Windows applications, but it does illustrate the point. Please don’t use this as anything other than an illustration—unless your boss makes you use it.

Listing 6.14 Using PreTranslateMessage to Handle Keystrokes in a Dialog Box


BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)
{
    if ( ( pMsg->message == WM_KEYDOWN ) &&
         ( pMsg->wParam == VK_RETURN ) )
    {
        // Simulate the normal TAB operation
        PostMessage( WM_NEXTDLGCTL, 0, false );
        // Return TRUE as we don’t want anyone else to handle this message
        return TRUE;
    }
    else
        // Let the base class try to handle the message
        return CDialog::PreTranslateMessage(pMsg);
}

Message Maps

Message maps are essentially a lookup table that links a message number to a member function of a class. The message map is implemented as an array of message numbers and pointers to functions that handle that message. Each derived message map is chained to its base class map, so that if the derived class doesn’t handle the message, the base class can have a chance at it.

MFC handles groups of messages differently. Generally, you can assume the categories that are described in the following sections.

Windows Messages

The Windows Messages category forms the bulk of the WM_* messages, with the notable exception of those in the following categories.

Control Notification Messages

The Control Notification Messages are traditionally passed as WM_COMMAND messages from a child window to its parent window. Newer common controls use a WM_NOTIFY message, rather than the much-abused WM_COMMAND message.

These first two message classifications are handled by classes derived from CWnd. In other words, these messages are handled by classes that represent real windows.

Command Messages

Command messages are WM_COMMAND messages from menus, accelerator keys, and toolbar buttons. MFC makes special provision for these messages, by enabling them to be handled not only by window classes, but also by any class derived from CCmdTarget. This includes CDocument, CDocTemplate, and the CWinApp object as well. Most of these messages are targeted to the frame window only—there is no other window that has context information to send the message elsewhere. For example, the menu is attached only to the main frame window; there is no data attached to menu items that would enable a message to be sent to a particular type of view window. This is why MFC provides an enhanced routing mechanism; it lets you decide where a message is most appropriately handled.

The first two classifications, Windows and Control Notification Messages, are dispatched directly to their destination window, where the message map is searched for a match with the appropriate parameters of the MSG structure.

MFC routes command messages first to the active top-level child window and then back down through the MFC window/class hierarchy. Thus, for example, in the case of a view window, the view class tries to handle the message and then passes it down to the document class. If the document class can’t handle the message, it passes it down to the document template, which in turn passes it to the main frame window and lastly to the application object.

To summarize this, you can assume that MFC routes messages first to the most specific class object. If that doesn’t handle the message, it is then passed to the next less specific object.

Which Is the Best Class to Handle Command Messages?

You might wonder why you’d want to handle Windows messages by classes that don’t have a window. I hope to explain why I think you’ll find it’s actually very useful to do so.

Although the traditional MDI model is apparently losing favor, I find its facility of having multiple views on a document is a good model to remember when deciding where a message should be handled—even if your application won’t have multiple views or multiple documents. Let me illustrate.

In an SDI application, it often doesn’t matter if a message is handled in the view or document class because there’s a one-to-one correspondence between the two, and from either class you can access the other. Let’s say you have a command in a word processor application that implements a Select All facility. In a simple SDI application, you could handle this command in the document class and keep the state variables that determine the selection in the CDocument-derived class. Because the view reflects the document, it will display the entire document as selected. However, if you consider that your application could have multiple views simultaneously displaying the document, you’ll realize that you’d probably want to have different selections for both views. Therefore, your decision of where to store the selection variables, as well as how to handle the selection commands, would immediately place them as the responsibility of the view class.



On the other hand, if you had a command that changed the font of the selected text, because you’d want all views to reflect the change in the font, you would probably choose to handle the command in the document class.

These contrived examples don’t present any situation that you couldn’t achieve in either case. However, in real-world situations, things can get rather messy if you get the design wrong by handling a command in an inappropriate class. Therefore, I find it’s best to always consider the MDI multiple-document, multiple-view situation.

What’s Behind the Message Map Macros

You can think of an MFC Message Map as the equivalent of a traditional non-MFC application’s message switch logic. In order to understand a little more about Message Maps, I’ll explain how they are constructed.

Listing 6.15 provides an example of a message map.

Listing 6.15 Message Map Example


BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    ON_WM_NCLBUTTONDOWN()
END_MESSAGE_MAP()

The map begins with the BEGIN_MESSAGE_MAP macro. If you decipher the macro, this expands to give your class the following items:

  An array named _messageEntries.
  A messageMap structure that points to this array and also the base class’s messageMap structure.
  A function named GetMessageMap that returns a pointer to the messageMap structure.


Note:  

The implementation is slightly different if you build your project with MFC linked statically, or as a DLL, but the net result is the same.


END_MESSAGE_MAP creates a predefined end for the message map array.

In your class’s header file, there’s also a corresponding DECLARE_MESSAGE_MAP macro that declares these variables and functions.

In the main body of the message map, you can see the comment block lines shown in Listing 6.16.

Listing 6.16 MFC Comment Blocks


    //{{AFX_MSG_MAP(CMainFrame)
    ...
    //}}AFX_MSG_MAP

These delimit the area that the Visual C++ Wizards use. You should not edit anything between these comments. If you do, you’ll probably prevent ClassWizard from working.

The Visual C++ wizards are a great help most of the time, but they don’t handle every eventuality. ClassWizard is there to handle the most common messages that you’re likely to come across, but it doesn’t know about all messages. At some point, you will likely have to add a handler that the wizard doesn’t cater to. In these circumstances, you can add the appropriate entry to the message map outside the comment blocks as shown by the ON_WM_NCLBUTTONDOWN line in Listing 6.15.

You’ll probably need to cross-check with the Visual C++ help, but there’s usually a direct macro equivalent to most messages. In Listing 6.15, the WM_NCLBUTTONDOWN message has an ON_WM_NCLBUTTONDOWN macro. Where there isn’t a direct macro, such as with a user-defined message number, you can use the general ON_MESSAGE macro—see your Visual C++ help for details.

Message Map Efficiency

In traditional Windows applications, the window procedure consisted of a large switch statement that checked each message against all the messages that the window needed to handle, and finally, if the message wasn’t handled, it was passed to the DefWindowProc routine.

MFC does away with these protracted switch statements and introduces its more refined message-routing mechanisms. You might assume that the extra code added by MFC to do this would result in slower operation. In some circumstances, such as a trivial Windows program, that may well be the case, but in real-world complex applications, the old switch statement method is grossly inefficient because the compiler generates code that performs an if-then-else operation for every case statement. For an unhandled message (which is probably most messages), every test is performed every time.

In MFC’s message maps, the message tests are performed in an efficient tight loop of code (which is better for processor caches). MFC also caches recent hits on the message maps so that frequent messages are routed more efficiently. This performance boost is probably most significant for the vast majority of unhandled messages.

Idle Processing

When your application has no messages to process, the Run function calls the OnIdle function. You can override this to have your application perform any background tasks.

OnIdle

Run passes a single value to OnIdle, which counts the number of times OnIdle has been successively called, and has little reflection on the real time your application has been idle. Each time your application processes a message, this count is reset to zero. If you need to determine the real idle time, the code example in Listing 6.17 shows how you can do this yourself.

Because OnIdle is intended for background task operation, you need to prevent your application from making Windows unresponsive. Therefore, you should only perform short processing tasks there. If you need to do a long operation, you should break up the task into a set of short states and do one operation on each call to OnIdle.

If your application needs further calls to OnIdle, return TRUE to have OnIdle called again (providing there are no messages pending). If your application has completed all its background tasks, return FALSE to relinquish processing and wait for the next message.


Note:  

Returning FALSE will not permanently stop OnIdle being called. As soon as your application processes all the messages in its queue, OnIdle will once again be called with a count of zero.


The documented place to override the OnIdle function is in your CWinApp-derived class. The example in Listing 6.17 determines the real idle time in seconds and displays it along with the count value on the status bar.

Listing 6.17 OnIdle Example


BOOL CSdiApp::OnIdle(LONG lCount)
{
    static CTime StartTime;
    CTimeSpan Span;
    /* If this is the first call to OnIdle, initialize the start time
     * Otherwise, calculate the idle time
     */
    if ( lCount == 0 )
    {
        StartTime = CTime::GetCurrentTime();
    Span = 0;
    }
    else
    {
        Span = CTime::GetCurrentTime() - StartTime;
    }
    char szMsg[100];
    wsprintf( szMsg, “Idle for %d seconds,
            lCount %d”, Span.GetTotalSeconds(), lCount );
    static_cast<CFrameWnd*>( AfxGetMainWnd() )->SetMessageText( szMsg );
    /* Call the default handler to run MFC debugging
     * facilities and temporary object cleanup
     */
    CWinApp::OnIdle(lCount);
    return true;
}


Note:  

It’s important to call the default OnIdle handler in your OnIdle routine, as it performs debug checks (in a DEBUG build) and garbage collection of temporary MFC objects. See “About Temporary Objects” in TN003 in your Visual C++ help for details.




An interesting, largely undocumented aspect is that the default implementation of CWinApp::OnIdle calls an OnIdle function for any document templates and their documents. Although these functions aren’t documented in the Visual C++ help, they are public functions and I would expect them to remain in later versions of Visual C++. You can therefore handle OnIdle not only in CWinApp, but also in your derived CDocument class.

This is a useful facility to remember if your OnIdle requirements are related to your document data rather than application-wide. For example, if your application is a word processor, it might be appropriate to perform background spelling and grammar checking in the CDocument::OnIdle handler.

In Visual C++ 6, the default CDocument::OnIdle does nothing, but you should still call it on the off chance that any future version of MFC could use the default behavior.

Idle Processing for Dialogs

If you look at the sample InitInstance for a dialog application (in Listing 6.5), you’ll see that the application has effectively ended when InitInstance returns. This is unlike other MFC applications that normally return TRUE from InitInstance and enter the Run function to process messages.

Because the dialog application doesn’t call Run, it also never benefits from the normal OnIdle handling. So how do you perform idle processing in a dialog application? The answer is the MFC WM_KICKIDLE message.

When you call a dialog’s DoModal function, MFC no longer creates a real modal dialog box. Instead, it fakes it by disabling any main frame window, and performs its own message loop to handle the “modal” dialog box. You can find the code in the MFC source code RunModalLoop function. When you know about WM_KICKIDLE, it’s easy to perform background tasks in a dialog application. All you have to do is add a handler for WM_KICKIDLE. You’ll need to add this manually, as illustrated in Listing 6.18, because the wizards don’t know about this message.

Listing 6.18 Message map for WM_KICKIDLE


BEGIN_MESSAGE_MAP(CDlgAppDlg, CDialog)
    //{{AFX_MSG_MAP(CDlgAppDlg)
    ...
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()


Note:  

The WM_KICKIDLE message map entry is added outside of the commented wizard block code so as not to confuse ClassWizard.


In your dialog class’s header file, add the following definition,

    afx_msg LRESULT OnKickIdle(WPARAM, LPARAM lCount);

and in the source module, add the function

LRESULT CDlgAppDlg::OnKickIdle(WPARAM, LPARAM lCount)
{
    // Add your idle code and return TRUE or FALSE in the same way as
    ÄOnIdle
}

Dialog Command Updating

In a dialog application, the normal MFC command update mechanism that enables and disables menu items and toolbar buttons doesn’t work—you need to add a few lines of code to make it happen.

You can do this quite easily by calling UpdateDialogControls from the WM_KICKIDLE handler:

LRESULT CDlgAppDlg::OnKickIdle(WPARAM, LPARAM lCount)
{
    UpdateDialogControls( this, TRUE );
    return 0;
}

The only other additions you need to make are to write the command handlers and add entries to the message map. The following code shows the function:

void CDlgAppDlg::OnUpdateCommandX(CCmdUI* pCmdUI)
{
   pCmdUI->Enable( your_expression_logic_to_enable_the_control );
}

To add the update command handler, modify the message map like this:

BEGIN_MESSAGE_MAP(CDlgAppDlg, CDialog)
    //{{AFX_MSG_MAP(CDlgAppDlg)
    ...
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
    ON_UPDATE_COMMAND_UI( IDM_COMMANDX, OnUpdateCommandX )
END_MESSAGE_MAP()

If you use UpdateDialogControls with its second parameter set to TRUE, most controls on your dialog that don’t have a handler routine are automatically disabled. I say “most” because MFC explicitly skips disabling controls with the following button styles:

            BS_AUTOCHECKBOX

            BS_AUTO3STATE

            BS_AUTORADIOBUTTON

            BS_GROUPBOX

I’m not aware of the reason behind this, but it seems reasonable to assume that auto check boxes and radio buttons may well be used to selectively enable and disable some of the controls on a dialog. Consequently, it’s unlikely that you’d want to disable the control that enables you to select other options on your dialog.

The Splash Screen Component

If you feel the urge to express your artistic talents in the form of a splash screen, it’s easy to add the code for one in your MFC application.

Although the default applications generated by AppWizard don’t give you a splash screen, you can add one using the additional component and controls with Visual C++. Click Project, Add to Project, Components and Controls. Double-click the Visual C++ Components and insert the Splash Screen component.

This adds a new class module (CSplashWnd) to your project and the following lines of code. This line is added to the CMainFrame::OnCreate handler to create the splash window:

    CSplashWnd::ShowSplashScreen(this);

These are added to InitInstance:

        CCommandLineInfo cmdInfo;
        ParseCommandLine(cmdInfo);

CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowSplash);

This last line enables the splash screen if there are no command-line parameters that dictate otherwise.

In the application’s PreTranslateMessage handler, this code handles messages for the splash screen window so that any keyboard or mouse messages destroy the splash window.

    if (CSplashWnd::PreTranslateAppMessage(pMsg))
        return TRUE;

In order to remove the splash screen window automatically, the splash window starts a timer for itself. When the timer expires, the splash window self-destructs.

That’s the easy bit—now you need to create your artwork. Have fun!

Summary

In this chapter, I’ve tried to cover the most common aspects of the CWinApp class, application lifetime, and the message routing architecture. In doing so, I’ve touched on other areas, such as the document/view architecture, and passed on a few tips learned from my own experiences with MFC. When you come across problems in your own development, it’s always worth remembering that the MFC source code is provided with Visual C++ so that you can delve deeper and gain a better understanding. I hope that you find this information helpful in your own MFC applications, and I encourage you to investigate the inner workings of MFC.